package co.yixiang.modules.connect.serviceimpl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import co.yixiang.cache.Cache;
import co.yixiang.cache.CachePrefix;
import co.yixiang.common.enums.ResultCode;
import co.yixiang.common.exception.ServiceException;
import co.yixiang.common.service.impl.BaseServiceImpl;
import co.yixiang.modules.connect.entity.domain.Connect;
import co.yixiang.modules.connect.entity.dto.ConnectQueryDTO;
import co.yixiang.modules.connect.entity.dto.WechatMPLoginParams;
import co.yixiang.modules.connect.entity.enums.ConnectEnum;
import co.yixiang.modules.connect.mapper.ConnectMapper;
import co.yixiang.modules.connect.service.ConnectService;
import co.yixiang.modules.member.entity.domain.YxUser;
import co.yixiang.modules.member.service.YxUserService;
import co.yixiang.modules.member.token.MemberTokenGenerate;
import co.yixiang.modules.security.entity.domain.Token;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.naming.NoPermissionException;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.*;

@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class ConnectServiceImpl extends BaseServiceImpl<ConnectMapper, Connect> implements ConnectService {

    @Autowired
    private MemberTokenGenerate memberTokenGenerate;
    @Autowired
    private YxUserService userService;
    @Autowired
    private Cache cache;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Token unionLoginCallback(String type, String unionid, String uuid, boolean longTerm) throws NoPermissionException {

        try {
            LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Connect::getUnionId, unionid);
            queryWrapper.eq(Connect::getUnionType, type);
            //查询绑定关系
            Connect connect = this.getOne(queryWrapper);
            if (connect == null) {
                throw new NoPermissionException("未绑定用户");
            }
            //查询会员
            YxUser member = userService.getById(connect.getUid());
            //如果未绑定会员，则把刚才查询到的联合登录表数据删除
            if (member == null) {
                this.remove(queryWrapper);
                throw new NoPermissionException("未绑定用户");
            }
            return memberTokenGenerate.createToken(member, longTerm);
        } catch (NoPermissionException e) {
            log.error("联合登陆失败：", e);
            throw e;
        }
    }

    @Override
    public void bind(String unionId, String type) {
//        AuthUser authUser = Objects.requireNonNull(UserContext.getCurrentUser());
//        Connect connect = new Connect(authUser.getId(), unionId, type);
//        this.save(connect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbind(String type) {
        LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<>();
        //queryWrapper.eq(Connect::getUid, UserContext.getCurrentUser().getId());
        queryWrapper.eq(Connect::getUnionType, type);
        this.remove(queryWrapper);
    }

    @Override
    @Transactional
    public Token miniProgramAutoLogin(WechatMPLoginParams params) {

        Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid());
        Map<String, String> map = new HashMap<>(3);
//        if (cacheData == null) {
//
//        } else {
//            map = (Map<String, String>) cacheData;
//        }
//得到微信小程序联合登陆信息
        JSONObject json = this.getConnect(params.getCode());
        //存储session key 后续登录用得到
        String sessionKey = json.getStr("session_key");
        String unionId = json.getStr("unionid");
        String openId = json.getStr("openid");
        map.put("sessionKey", sessionKey);
        map.put("unionId", unionId);
        map.put("openId", openId);
        cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid(), map, 900L);
        accessToken();
        //微信联合登陆参数
        return phoneMpBindAndLogin(map.get("sessionKey"), params, map.get("openId"), map.get("unionId"));
    }

    /**
     * 通过微信返回等code 获取openid 等信息
     *
     * @param code 微信code
     * @return 微信返回的信息
     */
    public JSONObject getConnect(String code) {
        // WechatConnectSettingItem setting = getWechatMPSetting();
        String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + "wx26ded6830740033c" + "&" + "secret=" + "20e410160af12df4ed9d549762215880" + "&" + "js_code=" + code + "&" + "grant_type=authorization_code";
        String content = HttpUtil.get(url);
        log.error(content);
        return JSONUtil.parseObj(content);
    }

    /**
     * 手机号 绑定 且 自动登录
     *
     * @param sessionKey 微信sessionKey
     * @param params     微信小程序自动登录参数
     * @param openId     微信openid
     * @param unionId    微信unionid
     * @return token
     */
    @Transactional(rollbackFor = Exception.class)
    public Token phoneMpBindAndLogin(String sessionKey, WechatMPLoginParams params, String openId, String unionId) {
        if(ObjectUtil.isEmpty(openId) && ObjectUtil.isEmpty(unionId)) {
            throw new ServiceException(ResultCode.PARAMS_ERROR, "获取用户的openId、unionId为空");
        }
        String encryptedData = params.getEncryptedData();
        String iv = params.getIv();
        //Vail(params);
        //JSONObject userInfo = this.getUserInfo(encryptedData, sessionKey, iv);
        //String phone = (String) userInfo.get("purePhoneNumber");
        //手机号登录
        YxUser member = new YxUser();
        if (CharSequenceUtil.isNotEmpty(unionId)) {
            Connect connect = this.getOne(new LambdaQueryWrapper<Connect>().eq(Connect::getUnionId, unionId).eq(Connect::getUnionType, ConnectEnum.WECHAT.name()));
            if (ObjectUtil.isNotEmpty(connect)) {
               // 找到用户信息
                member = userService.getOne(new LambdaQueryWrapper<YxUser>().eq(YxUser::getUid, connect.getUid()));
            }
        }//如果openid 不为空  则为账号绑定openid
        if (CharSequenceUtil.isNotEmpty(openId)) {
            Connect connect = this.getOne(new LambdaQueryWrapper<Connect>().eq(Connect::getUnionId, openId).eq(Connect::getUnionType, ConnectEnum.WECHAT_MP_OPEN_ID.name()));
            if (ObjectUtil.isNotEmpty(connect)) {
                member = userService.getOne(new LambdaQueryWrapper<YxUser>().eq(YxUser::getUid, connect.getUid()));
            }
        }

        //如果存在会员，则进行绑定微信openid 和 unionid，并且登录
        if (ObjectUtil.isNotEmpty(member) && ObjectUtil.isNotEmpty(member.getUid())) {
            bindMpMember(openId, unionId, member);
            return memberTokenGenerate.createToken(member, true);
        }
        //如果没有会员，则根据手机号注册会员
        YxUser newMember = new YxUser("wx_"+ IdUtil.simpleUUID(), "111111", "", params.getNickName(), params.getImage());
        userService.save(newMember);
        newMember = userService.getOne(new LambdaQueryWrapper<YxUser>().eq(YxUser::getUsername, newMember.getUsername()));
        bindMpMember(openId, unionId, newMember);

        return memberTokenGenerate.createToken(newMember, true);
    }

    public String accessToken() {
        String access_token = cache.getString(ConnectService.getMpAccessTokenKey());
        if (ObjectUtil.isEmpty(access_token)) {
            String s = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx26ded6830740033c&secret=20e410160af12df4ed9d549762215880");
            JSONObject jsonObject = JSONUtil.parseObj(s);
            access_token = jsonObject.getStr("access_token");
            cache.put(ConnectService.getMpAccessTokenKey(), access_token, 7000L);
        }
        return access_token;
    }

    public Boolean Vail(WechatMPLoginParams params) {
        String accessToken = accessToken();
        HashMap<String, Object> objectObjectHashMap = new HashMap<>();
         objectObjectHashMap.put("encrypted_msg_hash",params.getEncryptedData());
         String body = JSONUtil.toJsonStr(objectObjectHashMap);
        String post = HttpUtil.post("https://api.weixin.qq.com/wxa/business/checkencryptedmsg?access_token=" +  accessToken +"&offset_type=1", body);
        final JSONObject jsonObject = JSONUtil.parseObj(post);
        if (!"0".equals(jsonObject.getStr("errcode"))) {
            throw new  ServiceException(ResultCode.PARAMS_ERROR,post);
        }
        return Boolean.TRUE;
    }
    @Override
    public Connect queryConnect(ConnectQueryDTO connectQueryDTO) {

        LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(connectQueryDTO.getUserId()), Connect::getUid, connectQueryDTO.getUserId())
                .eq(CharSequenceUtil.isNotEmpty(connectQueryDTO.getUnionType()), Connect::getUnionType, connectQueryDTO.getUnionType())
                .eq(CharSequenceUtil.isNotEmpty(connectQueryDTO.getUnionId()), Connect::getUnionId, connectQueryDTO.getUnionId());
        return this.getOne(queryWrapper);
    }

    @Override
    public void deleteByMemberId(String userId) {
        LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Connect::getUid, userId);
        this.remove(queryWrapper);
    }

    /**
     * 会员绑定 绑定微信小程序
     * <p>
     * 如果openid 已经绑定其他账号，则这里不作处理，如果未绑定，则绑定最新的会员
     * 这样，微信小程序注册之后，其他app 公众号页面，都可以实现绑定自动登录功能
     * </p>
     *
     * @param openId    微信openid
     * @param unionId  微信unionid
     * @param member   会员
     */
    private void bindMpMember(String openId, String unionId, YxUser member) {
        //如果unionid 不为空  则为账号绑定unionid
        if (CharSequenceUtil.isNotEmpty(unionId)) {
            LambdaQueryWrapper<Connect> lambdaQueryWrapper = new LambdaQueryWrapper();
            lambdaQueryWrapper.eq(Connect::getUnionId, unionId);
            lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT.name());
            List<Connect> connects = this.list(lambdaQueryWrapper);
            if (connects.isEmpty()) {
                Connect connect = new Connect();
                connect.setUnionId(unionId);
                connect.setUid(member.getUid());
                connect.setUnionType(ConnectEnum.WECHAT.name());
                this.save(connect);
            }
        }//如果openid 不为空  则为账号绑定openid
        if (CharSequenceUtil.isNotEmpty(openId)) {
            LambdaQueryWrapper<Connect> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Connect::getUnionId, openId);
            lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT_MP_OPEN_ID.name());
            List<Connect> connects = this.list(lambdaQueryWrapper);
            if (connects.isEmpty()) {
                Connect connect = new Connect();
                connect.setUnionId(openId);
                connect.setUid(member.getUid());
                connect.setUnionType(ConnectEnum.WECHAT_MP_OPEN_ID.name());
                this.save(connect);
            }
        }

    }



    /**
     * 解密，获取微信信息
     *
     * @param encryptedData 加密信息
     * @param sessionKey    微信sessionKey
     * @param iv            微信揭秘参数
     * @return 用户信息
     */
    public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {

        // log.info("encryptedData:{},sessionKey:{},iv:{}", encryptedData, sessionKey, iv);
        //被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        //加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
        //偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            //如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            //初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            //初始化
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, StandardCharsets.UTF_8);
                return JSONUtil.parseObj(result);
            }
        } catch (Exception e) {
            log.error("解密，获取微信信息错误", e);
        }
        throw new ServiceException(ResultCode.USER_CONNECT_ERROR);
    }
}
